package cn.supermarket.service.impl;

import cn.supermarket.ex.ServiceException;
import cn.supermarket.mapper.AdminMapper;
import cn.supermarket.pojo.dto.AdminAddNewDTO;
import cn.supermarket.pojo.dto.AdminLoginDTO;
import cn.supermarket.pojo.dto.ChangePasswordDTO;
import cn.supermarket.pojo.dto.RoleAddDTO;
import cn.supermarket.pojo.entity.*;

import cn.supermarket.pojo.vo.AdminDetailsVO;
import cn.supermarket.pojo.vo.AdminSimpleVO;
import cn.supermarket.pojo.vo.RolePermissionVO;
import cn.supermarket.service.iservice.IAdminService;
import cn.supermarket.utils.GetInfoUtils;
import cn.supermarket.utils.SnowFlakeGenerateIdWorker;
import cn.supermarket.web.State;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class AdminServiceImpl implements IAdminService {

    @Autowired
    private AdminMapper adminMapper;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * JWT数据的密钥
     */
    @Value("${supermarket.jwt.secret-key}")
    private String secretKey;

    @Override
    @Transactional
    public void addNew(AdminAddNewDTO adminAddNewDTO) {
        String username = adminAddNewDTO.getUsername();
        if (adminMapper.selectByUsername(username) != null) {
            throw new ServiceException(
                    State.ERR_USERNAME_DUPLICATE, "添加管理员失败,用户名已存在!"
            );
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String password = adminAddNewDTO.getPassword();
        String encode = bCryptPasswordEncoder.encode(password);
        log.info("加密前的密码：{}，加密后的密码：{}", password, encode);
        adminAddNewDTO.setPassword(encode);
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminAddNewDTO, admin);
        SnowFlakeGenerateIdWorker snowFlakeGenerateIdWorker = new SnowFlakeGenerateIdWorker(20, 20);
        admin.setId(snowFlakeGenerateIdWorker.nextId());
        admin.setIsEnable(1);
        admin.setLoginCount(0);
        admin.setGmtCreate(LocalDateTime.now());
        admin.setGmtModified(LocalDateTime.now());
        int row = adminMapper.insertAdmin(admin);
        if (row != 1) {

            throw new ServiceException(
                    State.ERR_INSERT, "服务器繁忙,添加管理员失败!请稍后再试!"
            );
        }
        Role role = adminMapper.selectRoleByName(admin.getPosition());
        if (role == null) {
            throw new ServiceException(
                    State.ERR_INSERT,"添加管理员失败,该职位不存在!"
            );
        }
        Long roleId = role.getId();
        adminMapper.insertAdminRole(admin.getId(),roleId);
    }

    @Override
    @Transactional
    public void updateEnableById(Long id) {
        AdminDetailsVO adminDetailsVO = adminMapper.selectAdminDetailsById(id);
        if (adminDetailsVO == null) {
            throw new ServiceException(
                    State.ERR_NOT_FOUND, "修改失败,该管理员不存在!"
            );
        }
        Integer isEnable = adminDetailsVO.getIsEnable();
        if (isEnable != 0) {
            int row = adminMapper.updateEnableById(id, 0, LocalDateTime.now());
            if (row != 1) {
                throw new ServiceException(
                        State.ERR_INSERT, "服务器繁忙,修改管理员信息失败!请稍后再试!"
                );
            }
        } else {
            int row = adminMapper.updateEnableById(id, 1, LocalDateTime.now());
            if (row != 1) {
                throw new ServiceException(
                        State.ERR_INSERT, "服务器繁忙,修改管理员信息失败!请稍后再试!"
                );
            }
        }
    }

    @Override
    @Transactional
    public void updateInformationById(AdminDetailsVO adminDetailsVO) {
        AdminDetailsVO resultById = adminMapper.selectAdminDetailsById(adminDetailsVO.getId());
        if (resultById == null) {
            throw new ServiceException(
                    State.ERR_NOT_FOUND, "修改信息失败,尝试访问的数据不存在"
            );
        }
        AdminSimpleVO adminSimpleVO = adminMapper.selectByUsername(adminDetailsVO.getUsername());
        if (adminSimpleVO != null) {
            throw new ServiceException(
                    State.ERR_USERNAME_DUPLICATE,"修改类别失败,该用户名已存在"
            );
        }
        adminDetailsVO.setGmtModified(LocalDateTime.now());
        int row = adminMapper.updateInformationById(adminDetailsVO);
        if (row != 1) {
            throw new ServiceException(
                    State.ERR_UPDATE, "服务器繁忙,修改管理员信息失败!请稍后再试!"
            );
        }
        Role role = adminMapper.selectRoleByName(adminDetailsVO.getPosition());
        if (role == null) {
            throw new ServiceException(
                    State.ERR_UPDATE,"修改管理员失败,该职位不存在!"
            );
        }
        Long roleId = role.getId();
        AdminRole adminRole = adminMapper.selectAdminRoleByAdminId(adminDetailsVO.getId());
        System.out.println(adminRole);
        if (adminRole != null){
            adminMapper.deleteAdminRoleByAdminId(adminDetailsVO.getId());
        }
        adminMapper.insertAdminRole(adminDetailsVO.getId(),roleId);
    }

    @Override
    public void changePassword(ChangePasswordDTO changePasswordDTO) {
        validateChangePassword(changePasswordDTO);
        AdminDetailsVO adminDetailsVO = adminMapper.selectAdminDetailsById(changePasswordDTO.getId());
        System.out.println("adminDetailsVO: " + adminDetailsVO);
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(changePasswordDTO.getPassword(), adminDetailsVO.getPassword());
        if (!matches){
            throw new ServiceException(State.ERR_BAD_REQUEST,"原密码输入错误,请重试");
        }
        String encodedNewPassword = bCryptPasswordEncoder.encode(changePasswordDTO.getPassword());
        System.out.println("encodedNewPassword: " + encodedNewPassword);
        ChangePassword changePassword = new ChangePassword();
        changePassword.setId(changePasswordDTO.getId());
        changePassword.setNewPassword(encodedNewPassword);
        changePassword.setGmtModified(LocalDateTime.now());
        System.out.println("changePassword: " + changePassword);
        int row = adminMapper.updatePasswordById(changePassword);
        System.out.println("row: " + row);
        if (row!=1){
            throw new ServiceException(
                    State.ERR_INSERT, "服务器繁忙,修改密码失败!请稍后再试!"
            );
        }
    }

    public void validateChangePassword(ChangePasswordDTO changePasswordDTO){
        if (changePasswordDTO.getPassword()==null){
            throw new ServiceException(State.ERR_BAD_REQUEST,"原密码为空");
        }
        if (changePasswordDTO.getAckNewPassword()==null){
            throw new ServiceException(State.ERR_BAD_REQUEST,"确认新密码为空");
        }
        if (changePasswordDTO.getNewPassword()==null){
            throw new ServiceException(State.ERR_BAD_REQUEST,"新密码为空");
        }
        if (!StringUtils.equals(changePasswordDTO.getNewPassword(),changePasswordDTO.getAckNewPassword())){
            throw new ServiceException(State.ERR_BAD_REQUEST,"两次输入不正确");
        }
    }

    @Override
    public void deleteAdminById(Long id) {
        AdminDetailsVO adminDetailsVO = adminMapper.selectAdminDetailsById(id);
        if (adminDetailsVO == null) {
            throw new ServiceException(
                    State.ERR_NOT_FOUND,",注销管理员失败,该管理员不存在!"
            );
        }
        int row = adminMapper.deleteAdminById(id);
        if (row != 1) {
            throw new ServiceException(
                    State.ERR_UPDATE, "服务器繁忙,删除管理员失败!请稍后再试!"
            );
        }
    }

    @Override
    public List<AdminDetailsVO> list() {
        List<AdminDetailsVO> list = adminMapper.list();
        return list;
    }

    @Override
    public void addRole(RoleAddDTO roleAddDTO) {
        System.out.println(roleAddDTO.getName());
        Role result = adminMapper.selectRoleByName(roleAddDTO.getName());
        System.out.println(result);
        if (result != null) {
            throw new ServiceException(
                    State.ERR_NAME_DUPLICATE,"添加职位失败,该职位已存在!"
            );
        }
        Role role = new Role();
        SnowFlakeGenerateIdWorker snowFlakeGenerateIdWorker = new SnowFlakeGenerateIdWorker(20, 20);
        BeanUtils.copyProperties(roleAddDTO,role);
        role.setId(snowFlakeGenerateIdWorker.nextId());
        role.setGmtCreate(LocalDateTime.now());
        role.setGmtModified(LocalDateTime.now());
        int row = adminMapper.insertRole(role);
        if (row!=1){
            throw new ServiceException(
                    State.ERR_INSERT, "服务器繁忙,添加职位失败!请稍后再试!"
            );
        }
    }

    @Override
    @Transactional
    public void addPermission(String roleName, String[] permissionNames) {
        List<RolePermissionVO> rolePermissionVOs = adminMapper.selectPermissionByRoleName(roleName);
        if (rolePermissionVOs != null) {
            adminMapper.deletePermissionByRoleId(adminMapper.selectRoleByName(roleName).getId());
        }
        RolePermission rolePermission = new RolePermission();
        SnowFlakeGenerateIdWorker snowFlakeGenerateIdWorker = new SnowFlakeGenerateIdWorker(20, 20);
        Long roleId = adminMapper.selectRoleByName(roleName).getId();
        for (String permissionName : permissionNames) {
            System.out.println(permissionName);
            Long permissionId = adminMapper.selectPermissionByName(permissionName).getId();
            rolePermission.setId(snowFlakeGenerateIdWorker.nextId());
            rolePermission.setRoleId(roleId);
            rolePermission.setPermissionId(permissionId);
            int row = adminMapper.insertRolePermission(rolePermission);
            if (row != 1){
                throw new ServiceException(
                        State.ERR_INSERT, "服务器繁忙,添加权限失败!请稍后再试!"
                );
            }
        }
    }

    @Override
    public String login(AdminLoginDTO adminLoginDTO) {
        // 准备被认证数据
        Authentication authentication
                = new UsernamePasswordAuthenticationToken(
                adminLoginDTO.getUsername(), adminLoginDTO.getPassword());
        // 调用AuthenticationManager验证用户名与密码
        // 执行认证，如果此过程没有抛出异常，则表示认证通过，如果认证信息有误，将抛出异常
        Authentication authenticate = authenticationManager.authenticate(authentication);

        // 如果程序可以执行到此处，则表示登录成功
        // 生成此用户数据的JWT
        // Claims
        User user = (User) authenticate.getPrincipal();
        System.out.println("从认证结果中获取Principal=" + user.getClass().getName());
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", user.getUsername());
        claims.put("permissions", JSON.toJSONString(user.getAuthorities()));
        System.out.println("即将向JWT中写入数据=" + claims);

        // JWT的组成部分：Header（头），Payload（载荷），Signature（签名）
        String jwt = Jwts.builder()
                // Header：指定算法与当前数据类型
                // 格式为： { "alg": 算法, "typ": "jwt" }
                .setHeaderParam(Header.CONTENT_TYPE, "HS256")
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                // Payload：通常包含Claims（自定义数据）和过期时间
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + 999999999))
                // Signature：由算法和密钥（secret key）这2部分组成
                .signWith(SignatureAlgorithm.HS256, secretKey)
                // 打包生成
                .compact();

        // 返回JWT数据
        return jwt;
    }
}